package main

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/zing-dev/general-protocol-lib/cfg"
	"github.com/zing-dev/general-protocol-lib/log"
	"github.com/zing-dev/general-protocol-lib/protocol/mqtt"
	"io"
	"net"
	"strconv"
	"strings"
)

const (
	SectionName = "TCP->MQTT"
	TopicAlarm  = "DFVS/Channel/Alarm"
	TopicFiber  = "DFVS/Channel/Fiber"

	FiberStatusNone    = 0
	FiberStatusNoExist = 1
	FiberStatusBreak   = 2
	FiberStatusTooLong = 3

	HeartBeat = "msgType:99,info:1"
	EndTag    = "#over#"
)

type Config struct {
	DeviceID   string `comment:"设备码全局唯一,例如 112233445566"`
	ServerPort int    `comment:"服务器端口,例如 2001"`
	MQTTUrl    string `comment:"MQTT服务器地址,例如 127.0.0.1:1883"`
}

func newConfig() *Config {
	config := new(Config)
	cfg.New().Register(func(c *cfg.Config) {
		section := c.File.Section(SectionName)
		if section.Comment == "" {
			section.Comment = fmt.Sprintf("项目名: %s", SectionName)
		}
		if len(section.Keys()) == 0 {
			config.DeviceID = "112233445566"
			config.MQTTUrl = "127.0.0.1:1883"
			config.ServerPort = 2001
			err := section.ReflectFrom(config)
			if err != nil {
				log.L.Fatal(fmt.Sprintf("%s 反射失败: %s", SectionName, err))
			}
			c.Save()
			log.L.Fatal("请修改配置文件")
		}
		err := section.MapTo(config)
		if err != nil {
			log.L.Fatal(fmt.Sprintf("映射错误: %s", err))
		}

		if config.DeviceID == "" {
			log.L.Fatal("请输入设备id")
		}

		if config.ServerPort == 0 {
			log.L.Fatal("请输入设备串口号")
		}

		if config.MQTTUrl == "" {
			log.L.Fatal("请输入MQTT地址")
		}
	}).Load()
	return config
}

type FiberStatus struct {
	// 光纤状态
	FiberStatus string
	// 断纤位置点
	FiberBreakLength float32
	// 实际长度
	FiberRealLength float32
	// 机器码
	SensorID string
	// 通道ID
	ChannelID float32
	// 推送时间
	PushTime string
}

func FiberStatusToString(status byte) string {
	switch status {
	case FiberStatusNone:
		return "None"
	case FiberStatusNoExist:
		return "NoFiber"
	case FiberStatusBreak:
		return "Break"
	case FiberStatusTooLong:
		return "TooLong"
	default:
		return ""
	}
}

func FiberAlarmToString(alarm byte) string {
	switch alarm {
	case 0:
		return "其他"
	case 1:
		return "工程机械行驶"
	case 2:
		return "挖掘类施工"
	case 3:
		return "破拆类施工"
	case 4:
		return "发电机"
	case 5:
		return "人工事件"
	case 6:
		return "其他事件"
	}
	return ""
}

func (a FiberStatus) ToBytes() []byte {
	data, _ := json.Marshal(a)
	return data
}

type FiberAlarm struct {
	TypeID         uint32
	TypeName       string
	Level          byte
	Possibility    float32
	CenterPosition float32
	EventWidth     float32
	FirstPushTime  string
	MaxIntensity   float32
	SensorID       string
	ChannelID      byte
	PushTime       string
	LastPushTime   string
}

func (a FiberAlarm) ToBytes() []byte {
	data, _ := json.Marshal(a)
	return data
}

type Response struct {
	HandleAlarmStatus byte // 标记位，1：成功，0：失败（接收到数据，处理失败）。
	ID                uint //报警信息ID，该ID为上层软件接收报警信息时的ID,非上层软件保存数据库后的ID。
}

func (r Response) ToBytes() []byte {
	return []byte(fmt.Sprintf("HandleAlarmStatus:%d,ID:%d", r.HandleAlarmStatus, r.ID))
}

type Request struct {
	MsgType      byte      // 预留消息
	Id           uint      // 在底层软件中的事件信息编号，同一设备中唯一。
	EquipNo      string    // 设备的Ip地址（与上层软件配置的设备同步对应）
	Port         uint      // 预留消息
	AreaNo       []string  // 报警防区，暂定。默认值为A。
	ModeType     []byte    // 行为模式定义，暂定。默认为1.
	Distance     []float32 // 报警点位置
	CreateTime   string    // 入侵时间
	RealtimeData string
}

func FromString(content string) (r *Request, err error) {
	var (
		msgType  int
		id       int
		port     int
		modeType int
		distance float64
	)
	data := strings.Split(content, "$")
	if len(data) != 9 {
		return nil, errors.New(fmt.Sprintf("原始数据<%s>属性个数不是9", content))
	}
	r = new(Request)
	s0 := strings.Split(data[0], ":")
	if len(s0) != 2 {
		return nil, errors.New(fmt.Sprintf("解析MsgType<%s>失败", data[0]))
	}
	msgType, err = strconv.Atoi(s0[1])
	if err != nil {
		return
	}
	r.MsgType = byte(msgType)

	s1 := strings.Split(data[1], ":")
	if len(s1) != 2 {
		return nil, errors.New(fmt.Sprintf("解析Id<%s>失败", data[1]))
	}
	id, err = strconv.Atoi(s1[1])
	if err != nil {
		return
	}
	r.Id = uint(id)

	s2 := strings.Split(data[2], ":")
	if len(s2) != 2 {
		return nil, errors.New(fmt.Sprintf("解析EquipNo<%s>失败", data[2]))
	}
	r.EquipNo = s2[1]

	s3 := strings.Split(data[3], ":")
	if len(s3) != 2 {
		return nil, errors.New(fmt.Sprintf("解析Port<%s>失败", data[3]))
	}
	port, err = strconv.Atoi(s3[1])
	if err != nil {
		return
	}
	r.Port = uint(port)

	// 解析防区
	s4 := strings.Split(data[4], ":")
	if len(s4) != 2 {
		return nil, errors.New(fmt.Sprintf("解析AreaNo<%s>失败", data[4]))
	}
	area := strings.Split(s4[1], "-")
	r.AreaNo = make([]string, len(area))
	for i, s := range area {
		r.AreaNo[i] = s
	}

	// 解析报警类型
	s5 := strings.Split(data[5], ":")
	if len(s5) != 2 {
		return nil, errors.New(fmt.Sprintf("解析ModeType<%s>失败", data[5]))
	}
	t := strings.Split(s5[1], "-")
	r.ModeType = make([]byte, len(t))
	for i, t := range t {
		modeType, err = strconv.Atoi(t)
		r.ModeType[i] = byte(modeType)
	}

	if len(r.ModeType) > len(r.AreaNo) {
		r.ModeType = r.ModeType[:len(r.AreaNo)]
	} else {
		r.AreaNo = r.AreaNo[:len(r.ModeType)]
	}

	// 解析报警位置
	s6 := strings.Split(data[6], ":")
	if len(s6) != 2 {
		return nil, errors.New(fmt.Sprintf("解析Distance<%s>失败", data[6]))
	}
	d := strings.Split(s6[1], "-")
	r.Distance = make([]float32, len(d))
	for i, d := range d {
		distance, err = strconv.ParseFloat(d, 32)
		r.Distance[i] = float32(distance)
	}
	if len(r.Distance) > len(r.AreaNo) {
		r.Distance = r.Distance[:len(r.AreaNo)]
	} else {
		r.AreaNo = r.AreaNo[:len(r.Distance)]
	}

	//解析 CreateTime:2022-12-12 12:12:12
	s7 := strings.Split(data[7], ":")
	if len(s7) != 4 {
		return nil, errors.New(fmt.Sprintf("解析CreateTime<%s>失败", data[7]))
	}
	r.CreateTime = fmt.Sprintf("%s:%s:%s", s7[1], s7[2], s7[3])

	s8 := strings.Split(data[8], ":")
	if len(s8) != 2 {
		r.RealtimeData = ""
	} else {
		r.RealtimeData = s8[1]
	}
	return
}

func main() {
	log.Init()

	config := newConfig()
	m := mqtt.New(context.Background(), mqtt.Config{
		Url: config.MQTTUrl,
	})
	m.Run()

	listener, err := net.Listen("tcp", fmt.Sprintf(":%d", config.ServerPort))
	if err != nil {
		log.L.Fatal(fmt.Sprintf("创建监听TCP失败: %s", err))
	}
	log.L.Info(fmt.Sprintf("创建TCP监听,端口:%d...", config.ServerPort))

	defer func(l net.Listener) {
		err := l.Close()
		if err != nil {
			log.L.Fatal(err)
		}
	}(listener)
	for {
		conn, err := listener.Accept()
		if err != nil {
			log.L.Error(fmt.Sprintf("接收TCP连接失败: %s", err))
			continue
		}
		log.L.Info("新的TCP客户端连接...")
		go func(c net.Conn) {
			var buff = bytes.Buffer{}
			var cache = make([]byte, 300)

		START:
			for {
				n, err := conn.Read(cache)
				if err != nil && err != io.EOF {
					_, _ = conn.Write(Response{}.ToBytes())
					log.L.Error(fmt.Sprintf("读数据异常: %s", err))
					_ = conn.Close()
					break
				}
				buff.Write(cache[:n])
				var data []byte
				for {
					b, err := buff.ReadByte()
					if err != nil {
						goto START
					}
					data = append(data, b)
					// 心跳
					if bytes.HasSuffix(data, []byte(HeartBeat)) {
						log.L.Info("接收到心跳信息...")
						goto START
					}
					if bytes.HasSuffix(data, []byte(EndTag)) {
						break
					}
					if len(data) > 500 {
						log.L.Error("数据长度超过500字节舍弃,重新获取数据")
						goto START
					}
				}
				request, err := FromString(string(data[:len(data)-len(EndTag)]))
				if err != nil {
					_, _ = conn.Write(Response{}.ToBytes())
					log.L.Error(fmt.Sprintf("解析数据异常: %s", err))
					continue
				}
				log.L.Warn(fmt.Sprintf("请注意在%.3f处有%d级报警...", request.Distance, request.MsgType))
				if m.Client.IsConnected() {
					for i := range request.AreaNo {
						switch request.ModeType[i] {
						case 7:
							token := m.Client.Publish(TopicFiber, 2, false, FiberStatus{
								ChannelID:        1,
								FiberStatus:      FiberStatusToString(FiberStatusBreak),
								FiberBreakLength: request.Distance[i],
								FiberRealLength:  request.Distance[i],
								SensorID:         config.DeviceID,
								PushTime:         request.CreateTime,
							}.ToBytes())
							if token.Wait() && token.Error() != nil {
								log.L.Error(fmt.Sprintf("MQTT发布光纤断纤失败:%s", token.Error()))
							} else {
								log.L.Warn("MQTT发布光纤断纤成功")
								_, _ = conn.Write(Response{
									HandleAlarmStatus: 1,
									ID:                request.Id,
								}.ToBytes())
							}
						default:
							token := m.Client.Publish(TopicAlarm, 2, false, FiberAlarm{
								TypeName:       FiberAlarmToString(request.ModeType[i]),
								Level:          2,
								ChannelID:      1,
								Possibility:    100,
								EventWidth:     5,
								FirstPushTime:  request.CreateTime,
								CenterPosition: request.Distance[i],
								SensorID:       config.DeviceID,
								PushTime:       request.CreateTime,
								LastPushTime:   request.CreateTime,
							}.ToBytes())
							if token.Wait() && token.Error() != nil {
								log.L.Error(fmt.Sprintf("MQTT发布报警失败:%s", token.Error()))
							} else {
								log.L.Warn("MQTT发布报警成功")
								_, _ = conn.Write(Response{
									HandleAlarmStatus: 1,
									ID:                request.Id,
								}.ToBytes())
							}
						}
					}
				} else {
					log.L.Error("MQTT未连接,不能发布报警信息")
				}
			}
		}(conn)
	}
}
